package com.jh.fcsm.service.sys.impl;

import com.jh.fcsm.beans.sys.SysDictItem;
import com.jh.fcsm.beans.sys.vo.DictTree;
import com.jh.fcsm.beans.sys.vo.SysDictVo;
import com.jh.fcsm.common.BaseServiceImpl;
import com.jh.fcsm.common.exception.ServiceException;
import com.jh.fcsm.common.redis.RedisUtil;
import com.jh.fcsm.constant.Constant;
import com.jh.fcsm.constant.RedisConstant;
import com.jh.fcsm.dao.sys.SysDictItemMapper;
import com.jh.fcsm.enums.CommonStatusEnum;
import com.jh.fcsm.service.sys.SysDictItemService;
import com.jh.fcsm.util.AssertUtil;
import com.jh.fcsm.util.ExampleUtil;
import com.jh.fcsm.util.TreeUtil;
import com.jh.fcsm.util.security.UUIDUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.entity.Example.Criteria;

import java.util.ArrayList;
import java.util.List;

/**
 * 字典分类
 *
 * @author szx
 * @date 2020-07-05 15:14:51
 */
@Service
public class SysDictItemServiceImpl extends BaseServiceImpl<SysDictItemMapper, SysDictItem> implements SysDictItemService {

    public static final Logger logger = LoggerFactory.getLogger(SysDictItemServiceImpl.class);

    @Autowired
    private SysDictItemMapper basicDictItemMapper;
    @Autowired
    private RedisUtil redisUtil;

    /**
     * 查询字典列表
     *
     * @param dictItem 系统数据字典分类对象
     * @param isAdmin  是否是管理员
     * @return String 系统数据字典分类ID
     */
    @Override
    public List<SysDictItem> listItem(SysDictItem dictItem, boolean isAdmin) {
        Example example = new Example(SysDictItem.class);
        Criteria criteria = example.createCriteria();
        criteria.andEqualTo("yn", Constant.YES);
        if (!isAdmin) {
            // 不是管理员，只允许查看公开的字段
            criteria.andEqualTo("isOpen", Constant.DICT_IS_OPEN);
        }
        example.orderBy("orderVal").asc();
        return basicDictItemMapper.selectByExample(example);
    }

    /**
     * 新增数据字典分类
     *
     * @param dictItem 系统数据字典分类对象
     */
    @Override
    @Transactional(readOnly = false)
    public void addBasicDictItem(SysDictItem dictItem, SysDictVo dictVo) {
        AssertUtil.notNull(dictItem);
        String dictId = dictItem.getId();

        // 判断编码是否重复
        validateDictCodeUnique(dictId, dictItem.getItemCode());

        // 字典状态
        CommonStatusEnum statusEnum = CommonStatusEnum.getCommonStatusEnum(dictItem.getStatus());
        dictItem.setStatusTxt(statusEnum.getValue());
        String parentCode = dictItem.getParentCode();

        // 当前节点是根节点
        if (!StringUtils.hasText(parentCode) || Constant.ROOT.equals(parentCode)) {
            dictItem.setParentCode(Constant.ROOT);
        } else {
            // 当前节点是子节点
            // 验证父类是否存在，如果存在则将父节点的isLeaf属性修改为0，并且清空父节点的缓存
            validateAndUpdateParentNode(parentCode);
        }

        if (!StringUtils.hasText(dictId) || Constant.ROOT.equals(dictId)) {
            // 新增
            dictItem.setId(UUIDUtils.getUUID());
            dictItem.setIsLeaf(1);
            basicDictItemMapper.insertSelective(dictItem);
        } else {
            // 编辑
            this.basicDictItemMapper.updateByPrimaryKeySelective(dictItem);
        }
    }

    /**
     * 验证父类是否存在，并获取父类字典信息
     *
     * @param parentCode 父类字典编码
     */
    private void validateAndUpdateParentNode(String parentCode) {
        Example example = new Example(SysDictItem.class);
        example.createCriteria().andEqualTo("itemCode", parentCode).andEqualTo("yn", Constant.YES);
        SysDictItem sysDictItem = basicDictItemMapper.selectOneByExample(example);
        AssertUtil.notNull(sysDictItem, "父类节点不存在");

        // 父节点添加了子节点，所以不在是叶子节点
        SysDictItem record = new SysDictItem();
        record.setId(sysDictItem.getId());
        record.setIsLeaf(0);
        basicDictItemMapper.updateByPrimaryKeySelective(record);

        // 清空父节点缓存
        redisUtil.del(RedisConstant.DICT_TYPE_CODE + sysDictItem.getItemCode());
    }

    /**
     * 验证字典编码是否已存在
     *
     * @param dictId   字典ID
     * @param dictCode 字典编码
     */
    private void validateDictCodeUnique(String dictId, String dictCode) {
        Example example = new Example(SysDictItem.class);
        Criteria criteria = example.createCriteria();
        ExampleUtil.and(criteria, "itemCode", dictCode, ExampleUtil.EQUAL);
        if (StringUtils.hasText(dictId)) {
            ExampleUtil.and(criteria, "id", dictId, ExampleUtil.NON_EQUAL);
        }
        int count = basicDictItemMapper.selectCountByExample(example);
        if (count > 0) {
            throw new ServiceException("编码重复");
        }
    }

    /**
     * 更新数据字典
     *
     * @param dictItem 系统数据字典分类对象
     * @param isAdmin  是否是管理员
     */
    @Override
    @Transactional(readOnly = false)
    public void updateDictItem(SysDictItem dictItem, boolean isAdmin) {
        AssertUtil.notNull(dictItem);
        AssertUtil.notNull(dictItem.getId());

        // 校验CODE重复
        validateDictCodeUnique(dictItem.getId(), dictItem.getItemCode());
        // 从数据库获取字典
        SysDictItem dict = basicDictItemMapper.selectByPrimaryKey(dictItem.getId());
        if (dict == null || Constant.NO.equals(dict.getYn())) {
            throw new ServiceException("字典不存在");
        }
        // 如果当前节点存在父节点
        if (StringUtils.hasText(dict.getParentCode()) && !Constant.ROOT.equals(dict.getParentCode())) {
            validateAndUpdateParentNode(dict.getParentCode());
        }
        // 字典状态
        CommonStatusEnum statusEnum = CommonStatusEnum.getCommonStatusEnum(dictItem.getStatus());
        dictItem.setStatusTxt(statusEnum.getValue());

        basicDictItemMapper.updateByPrimaryKeySelective(dictItem);
    }

    /**
     * 删除数据字典分类
     *
     * @param id 数据字典ID
     */
    @Override
    @Transactional(readOnly = false)
    public void deleteById(String id) {
        AssertUtil.notNull(id);
        SysDictItem dict = basicDictItemMapper.selectByPrimaryKey(id);
        if (dict == null || Constant.NO.equals(dict.getYn())) {
            throw new ServiceException("字典不存在");
        }

        // 验证当前字典是否存在子节点
        validateDictChild(dict.getItemCode());
        // 判断当前节点是否存在兄弟节点，如果不存在，则需要修改父节点的状态
        validateExistSiblingAndUpdateParent(dict);
        // 删除当前节点
        Example deleteExample = new Example(SysDictItem.class);
        deleteExample.createCriteria().andEqualTo("id", id);
        this.mapper.deleteByExample(deleteExample);
    }

    /**
     * 验证字典是否存在兄弟节点，如果不存在，则修改父类字典状态
     *
     * @param dict 字典
     */
    private void validateExistSiblingAndUpdateParent(SysDictItem dict) {
        // 当前节点是根节点
        if (Constant.ROOT.equals(dict.getParentCode())) {
            return;
        }

        // 获取兄弟节点数量
        Example siblingExample = new Example(SysDictItem.class);
        siblingExample.createCriteria().andEqualTo("parentCode", dict.getParentCode()).andNotEqualTo("id", dict.getId()).andEqualTo("yn", Constant.YES);
        int count = basicDictItemMapper.selectCountByExample(siblingExample);

        // 不存在兄弟节点，则修改父类节点的状态
        if (count == 0) {
            // 从数据库获取父节点对象
            SysDictItem parent = findDictByCode(dict.getParentCode());
            AssertUtil.notNull(parent, "父节点不存在");

            // 更新父节点的状态
            SysDictItem record = new SysDictItem();
            record.setId(parent.getId());
            record.setIsLeaf(1);
            basicDictItemMapper.updateByPrimaryKeySelective(record);
        }
    }

    /**
     * 验证当前节点是否存在子节点
     *
     * @param code 字典Code
     */
    private void validateDictChild(String code) {
        Example example = new Example(SysDictItem.class);
        Criteria criteria = example.createCriteria();
        criteria.andEqualTo("parentCode", code);
        //查询是否存在子菜单
        int count = this.mapper.selectCountByExample(example);
        if (count > 0) {
            throw new ServiceException("存在子节点，操作失败");
        }
    }

    /**
     * 根据字典code得到字典的值
     *
     * @param code 字典编码
     * @return 字典
     */
    @Override
    public SysDictItem findDictByCode(String code) {
        Example example = new Example(SysDictItem.class);
        example.createCriteria().andEqualTo("itemCode", code).andEqualTo("yn", Constant.YES);
        return basicDictItemMapper.selectOneByExample(example);
    }

    /**
     * 根据字典CODE得到下级节点
     *
     * @param code 字典编码
     * @return 子节点
     */
    @Override
    public List<SysDictItem> findDictItemByDictCode(String code) {
        List<SysDictItem> list = (List<SysDictItem>) redisUtil.get(RedisConstant.DICT_TYPE_CODE + code);
        if (CollectionUtils.isEmpty(list)) {
            list = basicDictItemMapper.findDictItemByDictType(code);
            redisUtil.set(RedisConstant.DICT_TYPE_CODE + code, list, RedisConstant.REDIS_EXPIRE_ONE_DAY);
        }
        return list;
    }

    /**
     * 查询系统数据字典分类详情
     *
     * @param id 字典ID
     * @return 字典信息
     */
    @Override
    public SysDictItem findById(String id) {
        Example example = new Example(SysDictItem.class);
        Criteria criteria = example.createCriteria();
        criteria.andEqualTo("yn", Constant.YES).andEqualTo("itemCode", id);

        return basicDictItemMapper.selectOneByExample(example);
    }

    /**
     * 获取字典树
     *
     * @param admin 是否是管理员
     * @return 结果
     */
    @Override
    public List<DictTree> getDictTree(boolean admin) {
        List<SysDictItem> dictItems = listItem(new SysDictItem(), admin);
        return getDictTree(dictItems);
    }

    /**
     * 获取树结构
     *
     * @param dictItems 集合
     * @return 树
     */
    private List<DictTree> getDictTree(List<SysDictItem> dictItems) {
        List<DictTree> trees = new ArrayList<>();
        DictTree node;
        for (SysDictItem dict : dictItems) {
            node = new DictTree();
            BeanUtils.copyProperties(dict, node);
            node.setItemValue(dict.getItemValue());
            node.setId(dict.getItemCode());
            trees.add(node);
        }
        return TreeUtil.bulidNode(trees, Constant.ROOT);
    }
}
